// Copyright (c) 2023 Presto Labs Pte. Ltd.
// Author: leon, junxiao

#pragma once

#include <algorithm>
#include <memory>
#include <set>
#include <string>
#include <unordered_map>
#include <utility>
#include <vector>
#include <queue>

#include <glog/logging.h>
#include "coin/proto/coin_order_enums.pb.h"
#include "coin2/base/log.h"

namespace coin2::strategy::dex_strat {
using coin::proto::LiquiditySide;

struct LiquidityOrder {
  LiquiditySide liquidity_side;
  double provide_price_upper;
  double provide_price_lower;
  LiquidityOrder(LiquiditySide liquidity_side = LiquiditySide::UNKNOWN_LIQUIDITY_SIDE, double price_upper = 0.0, double price_lower = 0.0)
    : liquidity_side(liquidity_side), provide_price_upper(price_upper), provide_price_lower(price_lower) {}
  void SetVal(LiquiditySide liquidity_side = LiquiditySide::UNKNOWN_LIQUIDITY_SIDE, double price_upper = 0.0, double price_lower = 0.0) {
    this->liquidity_side = liquidity_side;
    this->provide_price_upper = price_upper;
    this->provide_price_lower = price_lower;
  }
};

struct LiquidityStatus {
  std::optional<double> cur_price;
  std::optional<double> price_upper;
  std::optional<double> price_lower;
  std::optional<double> amount;
  std::optional<double> cur_fee;
  LiquidityStatus() {}
  LiquidityStatus(std::optional<double> cur_price, std::optional<double> price_upper, std::optional<double> price_lower, 
  std::optional<double> amount, std::optional<double> cur_fee)
    : cur_price(cur_price), price_upper(price_upper), price_lower(price_lower), amount(amount), cur_fee(cur_fee) {}
};

class WindowEstimator {
public:
  WindowEstimator(int64_t max_record_cnt = 3600, double range_multiplier = 3.0);
  void InsertRecord(double price);
  bool Empty();
  bool HasEnoughRecord();
  double GetAvgPrice();
  double GetHalfWinSize();
  std::pair<double ,double> GetPriceWindow();
  void PrintInfo();
private:
  const int64_t max_record_cnt_;
  const double range_multiplier_;
  int64_t cur_record_cnt_;
  double cur_mean_;
  double cur_sqr_mean_;
  double half_win_size_;
  std::deque<double> record_queue_;
};

class BiasCalculator {
public:
  BiasCalculator(int64_t max_record_cnt = 3600, double withdraw_signal_bar = 3.0, double provide_signal_bar = 1.0);
  bool Empty();
  bool HasEnoughRecord();
  double GetAvgBiasVal();
  double GetLastBiasVal();
  void InsertRecord(double trade_price, double ref_price);
  bool IsPlusBiasWithdraw();
  bool IsMinusBiasWithdraw();
  bool IsPlusBiasProvide();
  bool IsMinusBiasProvide();
  bool ShouldWithdraw();
  bool ShouldProvide();
  void PrintInfo();
private:
  const int64_t max_record_cnt_;
  const double withdraw_signal_bar_;
  const double provide_signal_bar_;
  int64_t cur_record_cnt_;
  double cur_bias_val_;
  std::deque<double> record_queue_;

  double CalculateBias(double price_diff);
};

}  // namespace coin2::strategy::dex_strat